const path = require('path')
const fs = require('fs')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const { ProgressPlugin, DefinePlugin } = require('webpack')

const cssRegex = /\.css$/;
const sassRegex = /\.(scss|sass)$/;
const lessRegex = /\.less$/;

module.exports = ({
  env,
  isMain,
  name
}) => {
  const {
    pkgName,
    version,
    engines,
    dependencies,
    devDependencies,
  } = require(`./${name}/package.json`)
  const __APP_INFO__ = {
    pkg: { name: pkgName, version, engines, dependencies, devDependencies },
    buildTimestamp: Date.now(),
  };

  const isEnvProduction = env === 'production'
  const isEnvDevelopment = env === 'development'

  const isTypeScriptEnabled = fs.existsSync(__dirname, name, 'tsconfig.json')
  const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'

  const appSrc = path.resolve(__dirname, name, 'src')

  const getStyleLoaders = (cssOptions, preProcessor) => {
    const loaders = [
      isEnvDevelopment && require.resolve('style-loader'),
      isEnvProduction && {
        loader: MiniCssExtractPlugin.loader,
        // css is located in `static/css`, use '../../' to locate index.html folder
        // in production `paths.publicUrlOrPath` can be a relative path
        // options: paths.publicUrlOrPath.startsWith('.')
        //   ? { publicPath: '../../' }
        //   : {},
      },
      {
        loader: require.resolve('css-loader'),
        options: cssOptions,
      },
      {
        // Options for PostCSS as we reference these options twice
        // Adds vendor prefixing based on your specified browser support in
        // package.json
        loader: require.resolve('postcss-loader'),
        options: {
          postcssOptions: {
            // Necessary for external CSS imports to work
            // https://github.com/facebook/create-react-app/issues/2677
            ident: 'postcss',
            config: false,
            plugins: [
              'postcss-flexbugs-fixes',
              [
                'postcss-preset-env',
                {
                  autoprefixer: {
                    flexbox: 'no-2009',
                  },
                  stage: 3,
                },
              ],
              // Adds PostCSS Normalize as the reset css with default options,
              // so that it honors browserslist config in package.json
              // which in turn let's users customize the target behavior as per their needs.
              'postcss-normalize',
            ]
          },
          sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
        },
      },
    ].filter(Boolean);
    if (preProcessor) {
      loaders.push(
        {
          loader: require.resolve(preProcessor),
          options: {
            sourceMap: true,
            ...(preProcessor === 'sass-loader' ? {
              additionalData: `
                @use "@/styles/variables.scss" as *;
              `,
            } : {})
          },
        }
      );
    }
    return loaders;
  };

  return {
    // Webpack noise constrained to errors and warnings
    stats: 'errors-warnings',
    devtool: isEnvProduction
      ? shouldUseSourceMap
        ? 'source-map'
        : false
      : isEnvDevelopment && 'cheap-module-source-map',
    mode: env,
    entry: path.resolve(__dirname, name, 'src/main.ts'),
    output: {
      filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/[name].js',
      path: path.resolve(__dirname, name, 'dist'),
      library: {
        type: 'umd',
        name: name.replace(/-.{1}/g, (match) => {
          return match.replace('-', '').toLocaleUpperCase()
        })
      },
      // js chunk
      chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && 'static/js/[name].chunk.js',
      assetModuleFilename: 'static/media/[name].[hash][ext]',
      // publicPath: '.',
    },
    resolve: {
      extensions: ['.js', '.ts', '.json', '.jsx', '.tsx'],
      alias: {
        "@": path.resolve(__dirname, name, 'src')
      }
    },
    module: {
      rules: [
        {
          test: /\.vue$/,
          loader: require.resolve('vue-loader')
        },
        {
          oneOf: [
            // src
            {
              test: /\.(js|ts|jsx|tsx|mjs)$/,
              include: appSrc,
              loader: require.resolve('babel-loader'),
              options: {
                presets: [
                  [
                    require.resolve('@babel/preset-env'),
                    {
                      useBuiltIns: 'entry',
                      corejs: 3,
                      exclude: ['transform-typeof-symbol']
                    },
                  ],
                  //... 处理ts、tsx等loader信息
                  isTypeScriptEnabled && [require.resolve('@babel/preset-typescript'), {
                    allExtensions: true
                  }]
                ].filter(Boolean),
                plugins: [
                  isTypeScriptEnabled && [
                    require.resolve('@babel/plugin-proposal-decorators'),
                    false,
                  ],
                  [
                    require.resolve('@babel/plugin-proposal-class-properties'),
                    {
                      loose: true,
                    },
                  ],
                  [
                    require.resolve('@babel/plugin-proposal-private-methods'),
                    {
                      loose: true,
                    },
                  ],
                  [
                    require.resolve('@babel/plugin-proposal-private-property-in-object'),
                    {
                      loose: true,
                    },
                  ]
                ].filter(Boolean)
              }
            },
            // 非 src 资源编译
            {
              test: /\.(js|ts)$/,
              exclude: /@babel(?:\/|\\{1,2})runtime/,
              loader: require.resolve('babel-loader'),
              options: {
                babelrc: false,
                configFile: false,
                compact: false,
                presets: [
                  [
                    require.resolve('@babel/preset-env'),
                    {
                      // Allow importing core-js in entrypoint and use browserlist to select polyfills
                      useBuiltIns: 'entry',
                      corejs: 3,
                      // Exclude transforms that make all code slower
                      exclude: ['transform-typeof-symbol'],
                    }
                  ],
                ],
                cacheDirectory: true,
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,
                sourceMaps: shouldUseSourceMap,
                inputSourceMap: shouldUseSourceMap,
              },
            },
            {
              test: /\.(png|jpe?g|gif)$/,
              type: 'asset'
            },
            {
              test: cssRegex,
              use: getStyleLoaders({
                importLoaders: 1,
                sourceMap: isEnvProduction
                  ? shouldUseSourceMap
                  : isEnvDevelopment,
                modules: {
                  mode: 'icss',
                },
              }),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true,
            },
            {
              test: lessRegex,
              use: getStyleLoaders(
                {
                  importLoaders: 3,
                  sourceMap: isEnvProduction
                    ? shouldUseSourceMap
                    : isEnvDevelopment,
                  modules: {
                    mode: 'icss',
                  },
                },
                'less-loader'
              ),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true,
            },
            {
              test: sassRegex,
              use: getStyleLoaders(
                {
                  importLoaders: 3,
                  sourceMap: isEnvProduction
                    ? shouldUseSourceMap
                    : isEnvDevelopment,
                  modules: {
                    mode: 'icss',
                  },
                },
                'sass-loader'
              ),
              sideEffects: true,
            },
            // {
            //   exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
            //   type: 'asset/resource',
            // },
          ]
        }
      ]
    },
    plugins: [
      new VueLoaderPlugin(),
      new HtmlWebpackPlugin({
        template: path.resolve(__dirname, name, 'public/index.html'),
        title: `app-${name}`,
        filename: 'index.html'
      }),
      new ProgressPlugin(),
      new DefinePlugin({
        __APP_INFO__: JSON.stringify(__APP_INFO__)
      })
    ],
    optimization: {
      // 生产环境开启压缩
      // minimize: isEnvProduction,
      minimize: false,
      splitChunks: {
        chunks: 'all', // 共有三个值可选： initial (初始模块)、async(按需加载模块)和all(全部模块)
        minSize: 30000,
        minChunks: 2, // 模块被引用>=1次，便分割
        maxAsyncRequests: 5,  // 异步加载chunk的并发请求数量<=5
        maxInitialRequests: 5, // 一个入口并发加载的chunk数量<=3
        // name: true, // 默认由模块名+hash命名，名称相同时多个模块将合并为1个，可以设置为function
        automaticNameDelimiter: '~', // 命名分隔符
        cacheGroups: {
          init: {
            name: 'vendors',
            chunks: 'all',
            priority: -20,
            test: /[\\/]node_modules[\\/]/,
            /** 已经被拆分出来的bundle将被重用 */
            reuseExistingChunk: true,
            /** 忽略 splitChunks.minSize 、splitChunks.minChunks 、splitChunks.maxAsyncRequests 、splitChunks.maxInitialRequests，并始终以此为缓存组创建 chunk  */
            enforce: true
          },
          vue: {
            name: 'vue.vendors',
            // idHint: 'vendors',
            chunks: 'all',
            test: /[\\/]node_modules[\\/]@?vue/,
            priority: 10,
            enforce: true,
            reuseExistingChunk: true,
          },
          'vue-router': {
            name: 'vue-router.vendors',
            // idHint: 'vendors',
            chunks: 'all',
            test: /[\\/]node_modules[\\/]vue-router/,
            priority: 10,
            enforce: true,
            reuseExistingChunk: true,
          }
        }
      }
    }
  }
}